home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume24 / rcs / part01 next >
Encoding:
Internet Message Format  |  1991-03-05  |  51.7 KB

  1. Subject:  v24i001:  RCS source control system, Part01/12
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4. X-Checksum-Snefru: 825fd1d0 b2dd0696 15275f29 d497c069
  5.  
  6. Submitted-by: Adam Hammer <hammer@cs.purdue.edu>
  7. Posting-number: Volume 24, Issue 1
  8. Archive-name: rcs/part01
  9.  
  10. RCS is a revision control system that keeps audit trails, edit histories,
  11. and so on.  See the README file for more extensive details.  GNU diff,
  12. useful for RCS, will follow this posting.
  13.  
  14. #! /bin/sh
  15. # This is a shell archive.  Remove anything before this line, then feed it
  16. # into a shell via "sh file" or similar.  To overwrite existing files,
  17. # type "sh file -c".
  18. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  19. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  20. # Contents:  PACKNOTES README MANIFEST Makefile man src src/ci.c
  21. # Wrapped by rsalz@litchi.bbn.com on Thu Feb 21 14:36:53 1991
  22. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  23. echo If this archive is complete, you will see the following message:
  24. echo '          "shar: End of archive 1 (of 12)."'
  25. if test -f 'PACKNOTES' -a "${1}" != "-c" ; then 
  26.   echo shar: Will not clobber existing file \"'PACKNOTES'\"
  27. else
  28.   echo shar: Extracting \"'PACKNOTES'\" \(78 characters\)
  29.   sed "s/^X//" >'PACKNOTES' <<'END_OF_FILE'
  30. X# "rcs.ms" was split into 2 parts; to create it, do
  31. X    cat rcs.ms.[1-9] >rcs.ms
  32. END_OF_FILE
  33.   if test 78 -ne `wc -c <'PACKNOTES'`; then
  34.     echo shar: \"'PACKNOTES'\" unpacked with wrong size!
  35.   fi
  36.   # end of 'PACKNOTES'
  37. fi
  38. if test -f 'README' -a "${1}" != "-c" ; then 
  39.   echo shar: Will not clobber existing file \"'README'\"
  40. else
  41.   echo shar: Extracting \"'README'\" \(11703 characters\)
  42.   sed "s/^X//" >'README' <<'END_OF_FILE'
  43. X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
  44. X   Copyright 1990 by Paul Eggert
  45. X   Distributed under license by the Free Software Foundation, Inc.
  46. X
  47. XThis file is part of RCS.
  48. X
  49. XRCS is free software; you can redistribute it and/or modify
  50. Xit under the terms of the GNU General Public License as published by
  51. Xthe Free Software Foundation; either version 1, or (at your option)
  52. Xany later version.
  53. X
  54. XRCS is distributed in the hope that it will be useful,
  55. Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
  56. XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  57. XGNU General Public License for more details.
  58. X
  59. XYou should have received a copy of the GNU General Public License
  60. Xalong with RCS; see the file COPYING.  If not, write to
  61. Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  62. X
  63. XReport problems and direct all questions to:
  64. X
  65. X    rcs-bugs@cs.purdue.edu
  66. X
  67. X*/
  68. X
  69. X$Id: README,v 5.6 1990/12/13 06:54:04 eggert Exp $
  70. X
  71. XThis directory contains complete sources for RCS version 5.5.
  72. X
  73. X
  74. XInstallation notes:
  75. X
  76. X  RCS requires a diff that supports the -n option.
  77. X  Get GNU diff (version 1.15 or later) if your diff lacks -n.
  78. X
  79. X  RCS works best with a diff that supports -a, -L, and (diff3 only) -m.
  80. X  GNU diff supports these options.
  81. X
  82. X  Sources for RCS are in the src directory.
  83. X  Read the directions in src/Makefile to set up the options
  84. X  for building RCS on your system.
  85. X  If `make' fails to build src/conf.h, look in src/conf.error
  86. X  to see what went wrong in the src/conf.sh shell file.
  87. X  If all else fails, create src/conf.h manually by editing a
  88. X  copy of src/conf.heg.
  89. X
  90. X  Manual entries reside in man.
  91. X
  92. X  To test your installation of RCS, run the shell file src/rcstest.
  93. X
  94. X  Troff source for the paper `RCS--A System for Version Control', which
  95. X  appeared in _Software--Practice & Experience_, is in rcs.ms.
  96. X
  97. X
  98. XRCS compatibility notes:
  99. X
  100. X  RCS version 5 reads RCS files written by any RCS version released since 1982.
  101. X  It also writes RCS files that these older versions of RCS can read,
  102. X  unless you use one of the following new features:
  103. X
  104. X    checkin times after 1999/12/31 23:59:59 GMT
  105. X    checking in non-text files
  106. X    non-Ascii symbolic names
  107. X    rcs -bX, where X is nonempty
  108. X    rcs -kX, where X is not `kv'
  109. X    RCS files that exceed hardcoded limits in older RCS versions
  110. X
  111. X
  112. XFeatures new to RCS version 5 include:
  113. X
  114. X  RCS can check in arbitrary files, not just text files, if diff -a works.
  115. X  RCS can merge lines containing just a single `.' if diff3 -m works.
  116. X  GNU diff supports the -a and -m options.
  117. X
  118. X  RCS can now be installed as a setgid or setuid program
  119. X  if the setegid() and seteuid() system calls work.
  120. X  Setid privileges yield extra security if RCS files are protected so that
  121. X  only the effective group or user can write RCS directories.
  122. X  RCS uses the real group and user for all accesses other than to RCS files.
  123. X  On older hosts lacking setegid() and seteuid(), RCS uses the effective group
  124. X  and user for all accesses; formerly it was inconsistent.
  125. X
  126. X  New options to co, rcsdiff, and rcsmerge give more flexibility to keyword
  127. X  substitution.
  128. X
  129. X    -kkv substitutes the default `$Keyword: value $' for keyword strings.
  130. X    However, a locker's name is inserted only as a file is being locked,
  131. X    i.e. by `ci -l' and `co -l'.  This is normally the default.
  132. X
  133. X    -kkvl acts like -kkv, except that a locker's name is always inserted
  134. X    if the given revision is currently locked.  This was the default in
  135. X    version 4.  It is now the default only with when using rcsdiff to
  136. X    compare a revision to a working file whose mode is that of a file
  137. X    checked out for changes.
  138. X
  139. X    -kk substitutes just `$Keyword$', which helps to ignore keyword values
  140. X    when comparing revisions.
  141. X
  142. X    -ko retrieves the old revision's keyword string, thus bypassing keyword
  143. X    substitution.
  144. X
  145. X    -kv retrieves just `value'.  This can ease the use of keyword values, but
  146. X    it is dangerous because it causes RCS to lose track of where the keywords
  147. X    are, so for safety the owner write permission of the working file is
  148. X    turned off when -kv is used; to edit the file later, check it out again
  149. X    without -kv.
  150. X
  151. X  rcs -ko sets the default keyword substitution to be in the style of co -ko,
  152. X  and similarly for the other -k options.  This can be useful with binary file
  153. X  formats that cannot tolerate changing the lengths of keyword strings.
  154. X  However it also renders a RCS file readable only by RCS version 5 or later.
  155. X  Use rcs -kkv to restore the usual default substitution.
  156. X
  157. X  RCS can now be used by development groups that span timezone boundaries.
  158. X  All times are now displayed in GMT, and GMT is the default timezone.
  159. X  To use local time with co -d, append ` LT' to the time.
  160. X  When interchanging RCS files with sites running older versions of RCS,
  161. X  users may encounter discrepancies of up to 13 hours in old time stamps.
  162. X  The list of timezone names has been modernized.
  163. X
  164. X  Dates are now displayed using four-digit years, not two-digit years.
  165. X  Years given in -d options must now have four digits.
  166. X  This change is required for RCS to continue to work after 1999/12/31.
  167. X  The form of dates in version 5 RCS files will not change until 2000/01/01,
  168. X  so in the meantime RCS files can still be interchanged with sites
  169. X  running older versions of RCS.  To make room for the longer dates,
  170. X  rlog now outputs `lines: +A -D' instead of `lines added/del: A/D'.
  171. X
  172. X  To help prevent diff programs that are broken or have run out of memory
  173. X  from trashing an RCS file, ci now checks diff output more carefully.
  174. X
  175. X  ci -k now handles the Log keyword, so that checking in a file
  176. X  with -k does not normally alter the file's contents.
  177. X
  178. X  RCS no longer outputs white space at the ends of lines
  179. X  unless the original working file had it.
  180. X  For consistency with other keywords,
  181. X  a space, not a tab, is now output after `$Log:'.
  182. X  Rlog now puts lockers and symbolic names on separate lines in the output
  183. X  to avoid generating lines that are too long.
  184. X  A similar fix has been made to lists in the RCS files themselves.
  185. X
  186. X  RCS no longer outputs the string `Locker: ' when expanding Header or Id
  187. X  keywords.  This saves space and reverts back to version 3 behavior.
  188. X
  189. X  The default branch is not put into the RCS file unless it is nonempty.
  190. X  Therefore, files generated by RCS version 5 can be read by RCS version 3
  191. X  unless they use the default branch feature introduced in version 4.
  192. X  This fixes a compatibility problem introduced by version 4.
  193. X
  194. X  RCS can now emulate older versions of RCS; see `co -V'.
  195. X  This may be useful to overcome compatibility problems
  196. X  due to the above changes.
  197. X
  198. X  Programs like Emacs can now interact with RCS commands via a pipe:
  199. X  the new -I option causes ci, co, and rcs to run interactively,
  200. X  even if standard input is not a terminal.
  201. X  These commands now accept multiple inputs from stdin separated by `.' lines.
  202. X
  203. X  ci now silently ignores the -t option if the RCS file already exists.
  204. X  This simplifies some shell scripts and improves security in setuid sites.
  205. X
  206. X  Descriptive text may be given directly in an argument of the form -t-string.
  207. X
  208. X  The character set for symbolic names has been upgraded
  209. X  from Ascii to ISO 8859.
  210. X
  211. X  rcsdiff now passes through all options used by GNU diff;
  212. X  this is a longer list than 4.3BSD diff.
  213. X
  214. X  merge's new -L option gives tags for merge's overlap report lines.
  215. X  This ability used to be present in a different, undocumented form;
  216. X  the new form is chosen for compatibility with GNU diff3's -L option.
  217. X
  218. X  rcsmerge and merge now have a -q option, just like their siblings do.
  219. X
  220. X  RCS now attempts to ignore parts of an RCS file that look like they come
  221. X  from a future version of RCS.
  222. X
  223. X  When properly configured, RCS now strictly conforms with Posix 1003.1-1988.
  224. X  Normally, RCS file names contain `,', which is outside the Posix portable
  225. X  filename character set; but in impoverished Posix environments, you can
  226. X  compile RCS so that the RCS file for Foo is named just RCS/Foo.
  227. X  RCS can still be compiled in non-Posix traditional Unix environments,
  228. X  and can use common BSD and USG extensions to Posix.
  229. X  RCS is a conforming ANSI C program, and also compiles under traditional C.
  230. X
  231. X  Arbitrary limits on internal table sizes have been removed.
  232. X  The only limit now is the amount of memory available via malloc().
  233. X
  234. X  File temporaries, lock files, signals, and system call return codes
  235. X  are now handled more cleanly, portably, and quickly.
  236. X  Some race conditions have been removed.
  237. X
  238. X  A new compile-time option RCSPREFIX lets administrators avoid absolute path
  239. X  names for subsidiary programs, trading speed for flexibility.
  240. X
  241. X  The configuration procedure is now more automatic.
  242. X
  243. X  Snooping has been removed; it did not work in version 4.
  244. X
  245. X
  246. XVersion 4 was the first version distributed by FSF.
  247. XBeside bug fixes, features new to RCS version 4 include:
  248. X
  249. X  The notion of default branch has been added; see rcs -b.
  250. X
  251. X
  252. XVersion 3 was included in the 4.3BSD distribution.
  253. X
  254. X
  255. XFurther projects:
  256. X
  257. X  Improve performance when checking out branch revisions;
  258. X  see the `piece table' comments in rcs.ms.
  259. X  Joe Berkovitz of Stratus has written some fast revision extraction code;
  260. X  unfortunately there wasn't enough time to integrate it into RCS version 5.
  261. X  It's probably best to use mmap() here if available.
  262. X
  263. X  Let the user mark an RCS revision as deleted; checking out such a revision
  264. X  would result in no working file.  Similarly, using `co -d' with a date either
  265. X  before the initial revision or after the file was marked deleted should
  266. X  remove the working file.  For extra credit, extend the notion of `deleted' to
  267. X  include `renamed', i.e. when an RCS file gets renamed.
  268. X
  269. X  Use a better scheme for locking revisions; the current scheme requires
  270. X  changing the RCS file just to lock or unlock a revision.
  271. X  The new scheme should coexist as well as possible with older versions of RCS.
  272. X
  273. X  Permit multiple option-filename pairs, e.g. co -r1.4 a -r1.5 b.
  274. X
  275. X  Add rcs options for changing keyword names, e.g. XConsortium instead of Id.
  276. X
  277. X  If there are multiple locks by a user, ci should fall back on ci -k's
  278. X  method to figure out which version it is.
  279. X
  280. X  Add frozen branches a la SCCS.  In general, be able to emulate all of
  281. X  SCCS, so that an SCCS-to-RCS program can be practical.
  282. X
  283. X  Improve RCS's method for storing binary files.
  284. X  Although it is more efficient than SCCS's,
  285. X  the diff algorithm is still line oriented,
  286. X  and often generates long output for minor changes to an executable file.
  287. X
  288. X  Port binary file handling to non-Unix hosts where fopen(F,"r") and
  289. X  fopen(F,"rb") are quite different beasts.
  290. X
  291. X  Extend the grammar of RCS files so that keywords need not be in a fixed order.
  292. X
  293. X  Clean up the source code with a consistent indenting style.
  294. X
  295. X  Update the date parser to use the more modern getdate.y by Bellovin, Salz,
  296. X  and Berets.
  297. X
  298. X  Internationalize messages; unfortunately, there's no common standard yet.
  299. X
  300. X  Prune the unnecessary keyword substitution baggage from the rcs command.
  301. X
  302. X  Break up the code into a library so that it's easier to write new programs
  303. X  that manipulate RCS files.
  304. X
  305. X
  306. XCredits:
  307. X
  308. X  RCS was designed and built by Walter F. Tichy of Purdue University.
  309. X  RCS version 3 was released in 1983.
  310. X
  311. X  Thomas Narten, Dan Trinkle, and others of Purdue supported RCS through
  312. X  version 4.2, released in 1989.  Guy Harris of Sun contributed many porting
  313. X  fixes.  Paul Eggert of System Development Corporation contributed bug fixes
  314. X  and tuneups.  Jay Lepreau contributed 4.3BSD support.
  315. X
  316. X  Paul Eggert of Twin Sun wrote the changes for RCS version 5, released in
  317. X  1990.  Ideas for setgid support were contributed by Bill Hahn of Stratus.
  318. X  Test case ideas were contributed by Matt Cross of Stratus.
  319. X  Adam Hammer of Purdue QAed.
  320. END_OF_FILE
  321.   if test 11703 -ne `wc -c <'README'`; then
  322.     echo shar: \"'README'\" unpacked with wrong size!
  323.   fi
  324.   # end of 'README'
  325. fi
  326. if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  327.   echo shar: Will not clobber existing file \"'MANIFEST'\"
  328. else
  329.   echo shar: Extracting \"'MANIFEST'\" \(1641 characters\)
  330.   sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
  331. X   File Name        Archive #    Description
  332. X----------------------------------------------------------
  333. XPACKNOTES                  1    Warnings about long lines, etc
  334. XREADME                     1    
  335. XMANIFEST                   1    
  336. XCOPYING                    8    
  337. XMakefile                   1    
  338. Xman                        1    
  339. Xman/Makefile              12    
  340. Xman/ci.1                   9    
  341. Xman/co.1                   9    
  342. Xman/ident.1               12    
  343. Xman/merge.1               11    
  344. Xman/rcs.1                 11    
  345. Xman/rcsclean.1            11    
  346. Xman/rcsdiff.1             11    
  347. Xman/rcsfile.5             11    
  348. Xman/rcsfreeze.1           12    
  349. Xman/rcsintro.1            10    
  350. Xman/rcsmerge.1            11    
  351. Xman/rlog.1                 6    
  352. Xrcs.ms.01                  2    (part 1)
  353. Xrcs.ms.02                 11    (part 2)
  354. Xrcs_func.ms               10    
  355. Xsrc                        1    
  356. Xsrc/Makefile               3    
  357. Xsrc/ci.c                   1    
  358. Xsrc/co.c                   7    
  359. Xsrc/conf.heg              11    
  360. Xsrc/conf.sh                9    
  361. Xsrc/ident.c               11    
  362. Xsrc/maketime.c            10    
  363. Xsrc/merge.sh              11    
  364. Xsrc/partime.c              8    
  365. Xsrc/rcs.c                  3    
  366. Xsrc/rcsbase.h              6    
  367. Xsrc/rcsclean.sh           12    
  368. Xsrc/rcsdiff.c             10    
  369. Xsrc/rcsedit.c              6    
  370. Xsrc/rcsfcmp.c              7    
  371. Xsrc/rcsfnms.c              5    
  372. Xsrc/rcsfreeze.sh          11    
  373. Xsrc/rcsgen.c               9    
  374. Xsrc/rcskeep.c             10    
  375. Xsrc/rcskeys.c              2    
  376. Xsrc/rcslex.c               5    
  377. Xsrc/rcsmap.c              11    
  378. Xsrc/rcsmerge.c            10    
  379. Xsrc/rcsrev.c               7    
  380. Xsrc/rcssyn.c               8    
  381. Xsrc/rcsutil.c              4    
  382. Xsrc/rlog.c                 4    
  383. END_OF_FILE
  384.   if test 1641 -ne `wc -c <'MANIFEST'`; then
  385.     echo shar: \"'MANIFEST'\" unpacked with wrong size!
  386.   fi
  387.   # end of 'MANIFEST'
  388. fi
  389. if test -f 'Makefile' -a "${1}" != "-c" ; then 
  390.   echo shar: Will not clobber existing file \"'Makefile'\"
  391. else
  392.   echo shar: Extracting \"'Makefile'\" \(300 characters\)
  393.   sed "s/^X//" >'Makefile' <<'END_OF_FILE'
  394. XSUBDIR=    src man
  395. XDESTDIR=
  396. X
  397. Xall: ${SUBDIR}
  398. X
  399. X${SUBDIR}: FRC
  400. X    cd $@; make ${MFLAGS} DESTDIR=${DESTDIR}
  401. X
  402. Xinstall:
  403. X    for i in ${SUBDIR}; do \
  404. X        (cd $$i; make ${MFLAGS} DESTDIR=${DESTDIR} install); \
  405. X    done
  406. X
  407. Xclean:
  408. X    for i in ${SUBDIR}; do \
  409. X        (cd $$i; make ${MFLAGS} DESTDIR=${DESTDIR} clean); \
  410. X    done
  411. X
  412. XFRC:
  413. X
  414. END_OF_FILE
  415.   if test 300 -ne `wc -c <'Makefile'`; then
  416.     echo shar: \"'Makefile'\" unpacked with wrong size!
  417.   fi
  418.   # end of 'Makefile'
  419. fi
  420. if test ! -d 'man' ; then
  421.     echo shar: Creating directory \"'man'\"
  422.     mkdir 'man'
  423. fi
  424. if test ! -d 'src' ; then
  425.     echo shar: Creating directory \"'src'\"
  426.     mkdir 'src'
  427. fi
  428. if test -f 'src/ci.c' -a "${1}" != "-c" ; then 
  429.   echo shar: Will not clobber existing file \"'src/ci.c'\"
  430. else
  431.   echo shar: Extracting \"'src/ci.c'\" \(34353 characters\)
  432.   sed "s/^X//" >'src/ci.c' <<'END_OF_FILE'
  433. X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
  434. X   Copyright 1990 by Paul Eggert
  435. X   Distributed under license by the Free Software Foundation, Inc.
  436. X
  437. XThis file is part of RCS.
  438. X
  439. XRCS is free software; you can redistribute it and/or modify
  440. Xit under the terms of the GNU General Public License as published by
  441. Xthe Free Software Foundation; either version 1, or (at your option)
  442. Xany later version.
  443. X
  444. XRCS is distributed in the hope that it will be useful,
  445. Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
  446. XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  447. XGNU General Public License for more details.
  448. X
  449. XYou should have received a copy of the GNU General Public License
  450. Xalong with RCS; see the file COPYING.  If not, write to
  451. Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  452. X
  453. XReport problems and direct all questions to:
  454. X
  455. X    rcs-bugs@cs.purdue.edu
  456. X
  457. X*/
  458. X
  459. X/*
  460. X *                     RCS checkin operation
  461. X */
  462. X/*******************************************************************
  463. X *                       check revisions into RCS files
  464. X *******************************************************************
  465. X */
  466. X
  467. X
  468. X
  469. X/* $Log: ci.c,v $
  470. X * Revision 5.12  1990/12/31  01:00:12  eggert
  471. X * Don't use uninitialized storage when handling -{N,n}.
  472. X *
  473. X * Revision 5.11  1990/12/04  05:18:36  eggert
  474. X * Use -I for prompts and -q for diagnostics.
  475. X *
  476. X * Revision 5.10  1990/11/05  20:30:10  eggert
  477. X * Don't remove working file when aborting due to no changes.
  478. X *
  479. X * Revision 5.9  1990/11/01  05:03:23  eggert
  480. X * Add -I and new -t behavior.  Permit arbitrary data in logs.
  481. X *
  482. X * Revision 5.8  1990/10/04  06:30:09  eggert
  483. X * Accumulate exit status across files.
  484. X *
  485. X * Revision 5.7  1990/09/25  20:11:46  hammer
  486. X * fixed another small typo
  487. X *
  488. X * Revision 5.6  1990/09/24  21:48:50  hammer
  489. X * added cleanups from Paul Eggert.
  490. X *
  491. X * Revision 5.5  1990/09/21  06:16:38  hammer
  492. X * made it handle multiple -{N,n}'s.  Also, made it treat re-directed stdin
  493. X * the same as the terminal
  494. X *
  495. X * Revision 5.4  1990/09/20  02:38:51  eggert
  496. X * ci -k now checks dates more thoroughly.
  497. X *
  498. X * Revision 5.3  1990/09/11  02:41:07  eggert
  499. X * Fix revision bug with `ci -k file1 file2'.
  500. X *
  501. X * Revision 5.2  1990/09/04  08:02:10  eggert
  502. X * Permit adjacent revisions with identical time stamps (possible on fast hosts).
  503. X * Improve incomplete line handling.  Standardize yes-or-no procedure.
  504. X *
  505. X * Revision 5.1  1990/08/29  07:13:44  eggert
  506. X * Expand locker value like co.  Clean old log messages too.
  507. X *
  508. X * Revision 5.0  1990/08/22  08:10:00  eggert
  509. X * Don't require a final newline.
  510. X * Make lock and temp files faster and safer.
  511. X * Remove compile-time limits; use malloc instead.
  512. X * Permit dates past 1999/12/31.  Switch to GMT.
  513. X * Add setuid support.  Don't pass +args to diff.  Check diff's output.
  514. X * Ansify and Posixate.  Add -k, -V.  Remove snooping.  Tune.
  515. X * Check diff's output.
  516. X *
  517. X * Revision 4.9  89/05/01  15:10:54  narten
  518. X * changed copyright header to reflect current distribution rules
  519. X * 
  520. X * Revision 4.8  88/11/08  13:38:23  narten
  521. X * changes from root@seismo.CSS.GOV (Super User)
  522. X * -d with no arguments uses the mod time of the file it is checking in
  523. X * 
  524. X * Revision 4.7  88/08/09  19:12:07  eggert
  525. X * Make sure workfile is a regular file; use its mode if RCSfile doesn't have one.
  526. X * Use execv(), not system(); allow cc -R; remove lint.
  527. X * isatty(fileno(stdin)) -> ttystdin()
  528. X * 
  529. X * Revision 4.6  87/12/18  11:34:41  narten
  530. X * lint cleanups (from Guy Harris)
  531. X * 
  532. X * Revision 4.5  87/10/18  10:18:48  narten
  533. X * Updating version numbers. Changes relative to revision 1.1 are actually
  534. X * relative to 4.3
  535. X * 
  536. X * Revision 1.3  87/09/24  13:57:19  narten
  537. X * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  538. X * warnings)
  539. X * 
  540. X * Revision 1.2  87/03/27  14:21:33  jenkins
  541. X * Port to suns
  542. X * 
  543. X * Revision 4.3  83/12/15  12:28:54  wft
  544. X * ci -u and ci -l now set mode of working file properly.
  545. X * 
  546. X * Revision 4.2  83/12/05  13:40:54  wft
  547. X * Merged with 3.9.1.1: added calls to clearerr(stdin).
  548. X * made rewriteflag external.
  549. X * 
  550. X * Revision 4.1  83/05/10  17:03:06  wft
  551. X * Added option -d and -w, and updated assingment of date, etc. to new delta.
  552. X * Added handling of default branches.
  553. X * Option -k generates std. log message; fixed undef. pointer in reading of log.
  554. X * Replaced getlock() with findlock(), link--unlink with rename(),
  555. X * getpwuid() with getcaller().
  556. X * Moved all revision number generation to new routine addelta().
  557. X * Removed calls to stat(); now done by pairfilenames().
  558. X * Changed most calls to catchints() with restoreints().
  559. X * Directed all interactive messages to stderr.
  560. X * 
  561. X * Revision 3.9.1.1  83/10/19  04:21:03  lepreau
  562. X * Added clearerr(stdin) to getlogmsg() for re-reading stdin.
  563. X * 
  564. X * Revision 3.9  83/02/15  15:25:44  wft
  565. X * 4.2 prerelease
  566. X * 
  567. X * Revision 3.9  83/02/15  15:25:44  wft
  568. X * Added call to fastcopy() to copy remainder of RCS file.
  569. X *
  570. X * Revision 3.8  83/01/14  15:34:05  wft
  571. X * Added ignoring of interrupts while new RCS file is renamed;
  572. X * Avoids deletion of RCS files by interrupts.
  573. X *
  574. X * Revision 3.7  82/12/10  16:09:20  wft
  575. X * Corrected checking of return code from diff.
  576. X *
  577. X * Revision 3.6  82/12/08  21:34:49  wft
  578. X * Using DATEFORM to prepare date of checked-in revision;
  579. X * Fixed return from addbranch().
  580. X *
  581. X * Revision 3.5  82/12/04  18:32:42  wft
  582. X * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE. Updated
  583. X * field lockedby in removelock(), moved getlogmsg() before calling diff.
  584. X *
  585. X * Revision 3.4  82/12/02  13:27:13  wft
  586. X * added option -k.
  587. X *
  588. X * Revision 3.3  82/11/28  20:53:31  wft
  589. X * Added mustcheckin() to check for redundant checkins.
  590. X * Added xpandfile() to do keyword expansion for -u and -l;
  591. X * -m appends linefeed to log message if necessary.
  592. X * getlogmsg() suppresses prompt if stdin is not a terminal.
  593. X * Replaced keeplock with lockflag, fclose() with ffclose(),
  594. X * %02d with %.2d, getlogin() with getpwuid().
  595. X *
  596. X * Revision 3.2  82/10/18  20:57:23  wft
  597. X * An RCS file inherits its mode during the first ci from the working file,
  598. X * otherwise it stays the same, except that write permission is removed.
  599. X * Fixed ci -l, added ci -u (both do an implicit co after the ci).
  600. X * Fixed call to getlogin(), added call to getfullRCSname(), added check
  601. X * for write error.
  602. X * Changed conflicting identifiers.
  603. X *
  604. X * Revision 3.1  82/10/13  16:04:59  wft
  605. X * fixed type of variables receiving from getc() (char -> int).
  606. X * added include file dbm.h for getting BYTESIZ. This is used
  607. X * to check the return code from diff portably.
  608. X */
  609. X
  610. X#include "rcsbase.h"
  611. X
  612. Xstruct Symrev {
  613. X       const char *ssymbol;
  614. X       int override;
  615. X       struct Symrev * nextsym;
  616. X};
  617. X
  618. X/* rcsfcmp */
  619. Xint rcsfcmp P((const char*,const char*,const struct hshentry*));
  620. X
  621. X/* rcskeep */
  622. Xextern char prevdate[];
  623. Xextern struct buf prevauthor, prevrev, prevstate;
  624. Xint getoldkeys P((FILE*));
  625. X
  626. Xstatic const char *xpandfile P((const char*,const char*,const struct hshentry*));
  627. Xstatic const char *getdate P((void));
  628. Xstatic int addbranch P((struct hshentry*,struct buf*));
  629. Xstatic int addelta P((void));
  630. Xstatic int mustcheckin P((const char*,const struct hshentry*));
  631. Xstatic struct cbuf getlogmsg P((void));
  632. Xstatic struct hshentry *removelock P((struct hshentry*));
  633. Xstatic void cleanup P((void));
  634. Xstatic void incnum P((const char*,struct buf*));
  635. Xstatic void addassoclst P((int, char *));
  636. X
  637. Xstatic const char diff[] = DIFF;
  638. X
  639. Xstatic FILE *workptr;            /* working file pointer        */
  640. Xstatic const char *olddeltanum;        /* number of old delta        */
  641. Xstatic struct buf newdelnum;        /* new revision number        */
  642. Xstatic struct cbuf msg;
  643. Xstatic int exitstatus;
  644. Xstatic int forceciflag;            /* forces check in        */
  645. Xstatic int keepflag, keepworkingfile, rcsinitflag;
  646. Xstatic struct hshentries *gendeltas;    /* deltas to be generated    */
  647. Xstatic struct hshentry *targetdelta;    /* old delta to be generated    */
  648. Xstatic struct hshentry newdelta;    /* new delta to be inserted    */
  649. Xstatic struct Symrev *assoclst, *lastassoc;
  650. X
  651. XmainProg(ciId, "ci", "$Id: ci.c,v 5.12 1990/12/31 01:00:12 eggert Exp $")
  652. X{
  653. X    static const char cmdusage[] =
  654. X        "\nci usage: ci -{fklqru}[rev] -mmsg -{nN}name -sstate -t[textfile] -Vn file ...";
  655. X
  656. X    char altdate[datesize];
  657. X    const char *author, *krev, *rev, *state, *textfile;
  658. X    const char *diffilename, *expfilename;
  659. X    const char *workdiffname, *newworkfilename;
  660. X    int exit_stats;         /* return code for command invocations     */
  661. X    int lockflag;
  662. X    int r;
  663. X    int usestatdate; /* Use mod time of file for -d.  */
  664. X    mode_t newRCSmode; /* mode for RCS file */
  665. X    mode_t newworkmode; /* mode for working file */
  666. X    struct Symrev *curassoc;
  667. X    
  668. X    initid();
  669. X    catchints();
  670. X
  671. X    author = rev = state = textfile = nil;
  672. X    curassoc = assoclst = lastassoc = (struct Symrev *) nil;
  673. X    lockflag = false;
  674. X    altdate[0]= '\0'; /* empty alternate date for -d */
  675. X    usestatdate=false;
  676. X
  677. X        while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
  678. X                switch ((*argv)[1]) {
  679. X
  680. X                case 'r':
  681. X            keepworkingfile = lockflag = false;
  682. X                revno:  if ((*argv)[2]!='\0') {
  683. X                if (rev) warn("redefinition of revision number");
  684. X                                rev = (*argv)+2;
  685. X                        }
  686. X                        break;
  687. X
  688. X                case 'l':
  689. X                        keepworkingfile=lockflag=true;
  690. X                        goto revno;
  691. X
  692. X                case 'u':
  693. X                        keepworkingfile=true; lockflag=false;
  694. X                        goto revno;
  695. X
  696. X        case 'I':
  697. X            interactiveflag = true;
  698. X            goto revno;
  699. X
  700. X                case 'q':
  701. X                        quietflag=true;
  702. X                        goto revno;
  703. X
  704. X                case 'f':
  705. X                        forceciflag=true;
  706. X                        goto revno;
  707. X
  708. X                case 'k':
  709. X                        keepflag=true;
  710. X                        goto revno;
  711. X
  712. X                case 'm':
  713. X            if (msg.size) redefined('m');
  714. X            msg = cleanlogmsg(*argv+2, strlen(*argv+2));
  715. X            if (!msg.size)
  716. X                warn("missing message for -m option");
  717. X                        break;
  718. X
  719. X                case 'n':
  720. X            if ((*argv)[2] == '\0') {
  721. X                                error("missing symbolic name after -n");
  722. X                break;
  723. X                    }
  724. X                   checksid((*argv)+2);
  725. X                    addassoclst(false, (*argv)+2);
  726. X                break;
  727. X        
  728. X        case 'N':
  729. X            if ((*argv)[2] == '\0') {
  730. X                                error("missing symbolic name after -N");
  731. X                break;
  732. X                    }
  733. X                    checksid((*argv)+2);
  734. X                    addassoclst(true, (*argv)+2);
  735. X                break;
  736. X
  737. X                case 's':
  738. X                        if ((*argv)[2]!='\0'){
  739. X                if (state) redefined('s');
  740. X                checksid((*argv)+2);
  741. X                                state = (*argv)+2;
  742. X            } else
  743. X                warn("missing state for -s option");
  744. X                        break;
  745. X
  746. X                case 't':
  747. X                        if ((*argv)[2]!='\0'){
  748. X                if (textfile) redefined('t');
  749. X                                textfile = (*argv)+2;
  750. X                        }
  751. X                        break;
  752. X
  753. X        case 'd':
  754. X            if (altdate[0] || usestatdate)
  755. X                redefined('d');
  756. X            altdate[0] = 0;
  757. X            usestatdate = false;
  758. X            if ((*argv)[2])
  759. X                str2date(*argv+2, altdate);
  760. X            else
  761. X                usestatdate = true;
  762. X                        break;
  763. X
  764. X        case 'w':
  765. X                        if ((*argv)[2]!='\0'){
  766. X                if (author) redefined('w');
  767. X                checksid((*argv)+2);
  768. X                author = (*argv)+2;
  769. X            } else
  770. X                warn("missing author for -w option");
  771. X                        break;
  772. X
  773. X        case 'V':
  774. X            setRCSversion(*argv);
  775. X            break;
  776. X
  777. X
  778. X
  779. X                default:
  780. X            faterror("unknown option: %s%s", *argv, cmdusage);
  781. X                };
  782. X        }  /* end processing of options */
  783. X
  784. X    if (argc<1) faterror("no input file%s", cmdusage);
  785. X
  786. X        /* now handle all filenames */
  787. X        do {
  788. X        finptr=frewrite=NULL;
  789. X    fcopy = foutptr = NULL;
  790. X    workptr = NULL;
  791. X        targetdelta=nil;
  792. X        olddeltanum=nil;
  793. X    ffree();
  794. X
  795. X    switch (pairfilenames(argc, argv, rcswriteopen, false, false)) {
  796. X
  797. X        case -1:                /* New RCS file */
  798. X        rcsinitflag = true;
  799. X                break;
  800. X
  801. X        case 0:                 /* Error */
  802. X                continue;
  803. X
  804. X        case 1:                 /* Normal checkin with prev . RCS file */
  805. X        rcsinitflag = !Head;
  806. X        }
  807. X
  808. X        /* now RCSfilename contains the name of the RCS file, and
  809. X         * workfilename contains the name of the working file.
  810. X     * If the RCS file exists, finptr contains the file descriptor for the
  811. X         * RCS file. The admin node is initialized.
  812. X     * RCSstat is set.
  813. X         */
  814. X
  815. X    diagnose("%s  <--  %s\n", RCSfilename,workfilename);
  816. X
  817. X    errno = 0;
  818. X    if (!(workptr = fopen(workfilename,"r"))) {
  819. X        eerror(workfilename);
  820. X        continue;
  821. X    }
  822. X    if (!getfworkstat(fileno(workptr))) continue;
  823. X    newRCSmode =
  824. X          (rcsinitflag ? workstat.st_mode : RCSstat.st_mode)
  825. X        & ~(S_IWUSR|S_IWGRP|S_IWOTH);
  826. X    /* newRCSmode also adjusts mode of working file for -u and -l. */
  827. X    if (finptr && !checkaccesslist()) continue; /* give up */
  828. X
  829. X    krev = rev;
  830. X        if (keepflag) {
  831. X                /* get keyword values from working file */
  832. X        if (!getoldkeys(workptr)) continue;
  833. X        if (!rev  &&  !*(krev = prevrev.string)) {
  834. X            error("can't find a revision number in %s",workfilename);
  835. X                        continue;
  836. X                }
  837. X        if (*prevdate=='\0' && *altdate=='\0' && usestatdate==false)
  838. X            warn("can't find a date in %s", workfilename);
  839. X        if (!*prevauthor.string && !author)
  840. X            warn("can't find an author in %s", workfilename);
  841. X        if (!*prevstate.string && !state)
  842. X            warn("can't find a state in %s", workfilename);
  843. X        } /* end processing keepflag */
  844. X
  845. X        gettree(); /* reads in the delta tree.*/
  846. X
  847. X        /* expand symbolic revision number */
  848. X    if (!expandsym(krev,&newdelnum)) continue;
  849. X
  850. X        /* splice new delta into tree */
  851. X        if (!addelta()) continue;
  852. X
  853. X    if (rcsinitflag) {
  854. X        diagnose("initial revision: %s\n", newdelnum.string);
  855. X    } else  diagnose("new revision: %s; previous revision: %s\n",
  856. X             newdelnum.string, olddeltanum);
  857. X
  858. X    newdelta.num = newdelnum.string;
  859. X        newdelta.branches=nil;
  860. X        newdelta.lockedby=nil; /*might be changed by addlock() */
  861. X    newdelta.selector = true;
  862. X    /* set author */
  863. X    if (author!=nil)
  864. X        newdelta.author=author;     /* set author given by -w         */
  865. X    else if (keepflag && *prevauthor.string)
  866. X        newdelta.author=prevauthor.string; /* preserve old author if possible*/
  867. X    else    newdelta.author=getcaller();/* otherwise use caller's id      */
  868. X    if (state!=nil)
  869. X        newdelta.state=state;       /* set state given by -s          */
  870. X    else if (keepflag && *prevstate.string)
  871. X        newdelta.state=prevstate.string;   /* preserve old state if possible */
  872. X    else    newdelta.state=DEFAULTSTATE;/* otherwise use default state    */
  873. X    if (usestatdate) {
  874. X        time2date(workstat.st_mtime, altdate);
  875. X    }
  876. X    if (*altdate!='\0')
  877. X        newdelta.date=altdate;      /* set date given by -d           */
  878. X    else if (keepflag && *prevdate) /* preserve old date if possible  */
  879. X        newdelta.date = prevdate;
  880. X    else
  881. X        newdelta.date = getdate();  /* use current date               */
  882. X    /* now check validity of date -- needed because of -d and -k          */
  883. X    if (targetdelta!=nil &&
  884. X        cmpnum(newdelta.date,targetdelta->date) < 0) {
  885. X        error("Date %s precedes %s in existing revision %s.",
  886. X               newdelta.date,targetdelta->date, targetdelta->num);
  887. X        continue;
  888. X    }
  889. X
  890. X
  891. X    if (lockflag  &&  addlock(&newdelta) < 0) continue;
  892. X        curassoc = assoclst;
  893. X    while (curassoc) {
  894. X            if (!addsymbol(newdelta.num, curassoc->ssymbol, curassoc->override))
  895. X                break;
  896. X            curassoc = curassoc->nextsym;
  897. X    }
  898. X    if (curassoc) continue;
  899. X
  900. X    
  901. X        putadmin(frewrite);
  902. X        puttree(Head,frewrite);
  903. X    putdesc(false,textfile);
  904. X
  905. X
  906. X        /* build rest of file */
  907. X    if (rcsinitflag) {
  908. X                /* get logmessage */
  909. X                newdelta.log=getlogmsg();
  910. X        if (!putdftext(newdelnum.string,newdelta.log,workptr,frewrite,false)) continue;
  911. X        } else {
  912. X        diffilename = maketemp(0);
  913. X        workdiffname = workfilename;
  914. X        if (workdiffname[0] == '+') {
  915. X            /* Some diffs have options with leading '+'. */
  916. X            char *w = ftnalloc(char, strlen(workfilename)+3);
  917. X            workdiffname = w;
  918. X            *w++ = '.';
  919. X            *w++ = SLASH;
  920. X            VOID strcpy(w, workfilename);
  921. X        }
  922. X                if (&newdelta==Head) {
  923. X                        /* prepend new one */
  924. X            foutptr = NULL;
  925. X                        if (!(expfilename=
  926. X                  buildrevision(gendeltas,targetdelta,false,false))) continue;
  927. X                        if (!mustcheckin(expfilename,targetdelta)) continue;
  928. X                                /* don't check in files that aren't different, unless forced*/
  929. X                        newdelta.log=getlogmsg();
  930. X                        exit_stats = run((char*)nil,diffilename,
  931. X                diff DIFF_FLAGS, workdiffname, expfilename,
  932. X                (char*)nil);
  933. X            if (!WIFEXITED(exit_stats) || 1<WEXITSTATUS(exit_stats))
  934. X                            faterror ("diff failed");
  935. X            /* diff status is EXIT_TROUBLE on failure. */
  936. X            if (!putdftext(newdelnum.string,newdelta.log,workptr,frewrite,false)) continue;
  937. X            if (!putdtext(olddeltanum,targetdelta->log,diffilename,frewrite,true)) continue;
  938. X                } else {
  939. X                        /* insert new delta text */
  940. X            foutptr = frewrite;
  941. X                        if (!(expfilename=
  942. X                  buildrevision(gendeltas,targetdelta,false,false))) continue;
  943. X                        if (!mustcheckin(expfilename,targetdelta)) continue;
  944. X                                /* don't check in files that aren't different, unless forced*/
  945. X                        newdelta.log=getlogmsg();
  946. X                        exit_stats = run((char*)nil, diffilename,
  947. X                diff DIFF_FLAGS, expfilename, workdiffname,
  948. X                (char*)nil);
  949. X            if (!WIFEXITED(exit_stats) || 1<WEXITSTATUS(exit_stats))
  950. X                            faterror ("diff failed");
  951. X            if (!putdtext(newdelnum.string,newdelta.log,diffilename,frewrite,true)) continue;
  952. X                }
  953. X
  954. X                /* rewrite rest of RCS file */
  955. X                fastcopy(finptr,frewrite);
  956. X        ffclose(finptr); finptr=NULL; /* Help the file system. */
  957. X        }
  958. X    ffclose(frewrite); frewrite=NULL;
  959. X    ffclose(workptr); workptr=NULL;
  960. X    seteid();
  961. X    if ((r = chmod(newRCSfilename,newRCSmode)) == 0) {
  962. X        ignoreints();
  963. X        r = re_name(newRCSfilename,RCSfilename);
  964. X        keepdirtemp(newRCSfilename);
  965. X        restoreints();
  966. X    }
  967. X    setrid();
  968. X    if (r != 0) {
  969. X        eerror(RCSfilename);
  970. X        error("saved in %s", newRCSfilename);
  971. X        dirtempunlink();
  972. X                break;
  973. X        }
  974. X
  975. X        if (!keepworkingfile) {
  976. X        r = unlink(workfilename); /* Get rid of old file */
  977. X        } else {
  978. X        newworkmode = WORKMODE(newRCSmode,
  979. X            !(Expand == OLD_EXPAND  ||  !lockflag && StrictLocks)
  980. X        );
  981. X        /* Expand if !OLD_EXPAND, or if mode can't be fixed.  */
  982. X        if (
  983. X            Expand != OLD_EXPAND
  984. X        ||    (    workstat.st_mode != newworkmode
  985. X            &&    (r = chmod(workfilename,newworkmode)) < 0
  986. X            )
  987. X        ) {
  988. X            /* Expand keywords in file.  */
  989. X            locker_expansion = lockflag;
  990. X            newworkfilename=
  991. X            xpandfile(workfilename,workfilename /*for directory*/,&newdelta);
  992. X            if (!newworkfilename) continue; /* expand failed */
  993. X            if ((r = chmod(newworkfilename, newworkmode)) == 0) {
  994. X            ignoreints();
  995. X            r = re_name(newworkfilename,workfilename);
  996. X            keepdirtemp(newworkfilename);
  997. X            restoreints();
  998. X            }
  999. X        }
  1000. X        }
  1001. X    if (r != 0) {
  1002. X        eerror(workfilename);
  1003. X        continue;
  1004. X    }
  1005. X    diagnose("done\n");
  1006. X
  1007. X        } while (cleanup(),
  1008. X                 ++argv, --argc >=1);
  1009. X
  1010. X    tempunlink();
  1011. X    exitmain(exitstatus);
  1012. X}       /* end of main (ci) */
  1013. X
  1014. X    static void
  1015. Xcleanup()
  1016. X{
  1017. X    if (nerror) exitstatus = EXIT_FAILURE;
  1018. X    if (finptr) ffclose(finptr);
  1019. X    if (frewrite) ffclose(frewrite);
  1020. X    if (workptr) ffclose(workptr);
  1021. X    dirtempunlink();
  1022. X}
  1023. X
  1024. X#if lint
  1025. X#    define exiterr ciExit
  1026. X#endif
  1027. X    exiting void
  1028. Xexiterr()
  1029. X{
  1030. X    dirtempunlink();
  1031. X    tempunlink();
  1032. X    _exit(EXIT_FAILURE);
  1033. X}
  1034. X
  1035. X/*****************************************************************/
  1036. X/* the rest are auxiliary routines                               */
  1037. X
  1038. X
  1039. X    static int
  1040. Xaddelta()
  1041. X/* Function: Appends a delta to the delta tree, whose number is
  1042. X * given by newdelnum.  Updates Head, newdelnum, newdelnumlength,
  1043. X * olddeltanum and the links in newdelta.
  1044. X * Returns false on error, true on success.
  1045. X */
  1046. X{
  1047. X    register char *tp;
  1048. X    register unsigned i;
  1049. X    unsigned newdnumlength;  /* actual length of new rev. num. */
  1050. X
  1051. X    newdnumlength = countnumflds(newdelnum.string);
  1052. X
  1053. X    if (rcsinitflag) {
  1054. X                /* this covers non-existing RCS file and a file initialized with rcs -i */
  1055. X        if ((newdnumlength==0)&&(Dbranch!=nil)) {
  1056. X            bufscpy(&newdelnum, Dbranch);
  1057. X            newdnumlength = countnumflds(Dbranch);
  1058. X        }
  1059. X        if (newdnumlength==0) bufscpy(&newdelnum, "1.1");
  1060. X        else if (newdnumlength==1) bufscat(&newdelnum, ".1");
  1061. X        else if (newdnumlength>2) {
  1062. X            error("Branch point doesn't exist for %s.",newdelnum.string);
  1063. X                    return false;
  1064. X                } /* newdnumlength == 2 is OK;  */
  1065. X                olddeltanum=nil;
  1066. X                Head = &newdelta;
  1067. X                newdelta.next=nil;
  1068. X                return true;
  1069. X        }
  1070. X        if (newdnumlength==0) {
  1071. X                /* derive new revision number from locks */
  1072. X        switch (findlock(true, &targetdelta)) {
  1073. X
  1074. X          default:
  1075. X            /* found two or more old locks */
  1076. X            return false;
  1077. X
  1078. X          case 1:
  1079. X                    /* found an old lock */
  1080. X                    olddeltanum=targetdelta->num;
  1081. X                    /* check whether locked revision exists */
  1082. X            if (!genrevs(olddeltanum,(char*)nil,(char*)nil,(char*)nil,&gendeltas)) return false;
  1083. X                    if (targetdelta==Head) {
  1084. X                        /* make new head */
  1085. X                        newdelta.next=Head;
  1086. X                        Head= &newdelta;
  1087. X            incnum(olddeltanum, &newdelnum);
  1088. X            } else if (!targetdelta->next && countnumflds(olddeltanum)>2) {
  1089. X                        /* new tip revision on side branch */
  1090. X                        targetdelta->next= &newdelta;
  1091. X                        newdelta.next = nil;
  1092. X            incnum(olddeltanum, &newdelnum);
  1093. X                    } else {
  1094. X                        /* middle revision; start a new branch */
  1095. X            bufscpy(&newdelnum, "");
  1096. X            if (!addbranch(targetdelta,&newdelnum)) return false;
  1097. X                    }
  1098. X            return true; /* successful use of existing lock */
  1099. X
  1100. X          case 0:
  1101. X                    /* no existing lock; try Dbranch */
  1102. X                    /* update newdelnum */
  1103. X            if (StrictLocks || !myself(RCSstat.st_uid)) {
  1104. X            error("no lock set by %s",getcaller());
  1105. X                        return false;
  1106. X                    }
  1107. X                    if (Dbranch) {
  1108. X            bufscpy(&newdelnum, Dbranch);
  1109. X                    } else {
  1110. X            incnum(Head->num, &newdelnum);
  1111. X                    }
  1112. X            newdnumlength = countnumflds(newdelnum.string);
  1113. X                    /* now fall into next statement */
  1114. X                }
  1115. X        }
  1116. X        if (newdnumlength<=2) {
  1117. X                /* add new head per given number */
  1118. X                olddeltanum=Head->num;
  1119. X                if(newdnumlength==1) {
  1120. X                    /* make a two-field number out of it*/
  1121. X            if (cmpnumfld(newdelnum.string,olddeltanum,1)==0)
  1122. X            incnum(olddeltanum, &newdelnum);
  1123. X            else
  1124. X            bufscat(&newdelnum, ".1");
  1125. X                }
  1126. X        if (cmpnum(newdelnum.string,olddeltanum) <= 0) {
  1127. X                    error("deltanumber %s too low; must be higher than %s",
  1128. X              newdelnum.string, Head->num);
  1129. X                    return false;
  1130. X                }
  1131. X        if (!(targetdelta=removelock(Head))) return false;
  1132. X        if (!genrevs(olddeltanum,(char*)nil,(char*)nil,(char*)nil,&gendeltas)) return false;
  1133. X                newdelta.next=Head;
  1134. X                Head= &newdelta;
  1135. X        } else {
  1136. X                /* put new revision on side branch */
  1137. X                /*first, get branch point */
  1138. X        tp = newdelnum.string;
  1139. X        for (i = newdnumlength - (newdnumlength&1 ^ 1);  (--i);  )
  1140. X            while (*tp++ != '.')
  1141. X                ;
  1142. X        *--tp = 0; /* Kill final dot to get old delta temporarily. */
  1143. X        if (!(targetdelta=genrevs(newdelnum.string,(char*)nil,(char*)nil,(char*)nil,&gendeltas)))
  1144. X                     return false;
  1145. X        olddeltanum = targetdelta->num;
  1146. X        if (cmpnum(olddeltanum, newdelnum.string) != 0) {
  1147. X            error("can't find branchpoint %s", newdelnum.string);
  1148. X                    return false;
  1149. X                }
  1150. X        *tp = '.'; /* Restore final dot. */
  1151. X        if (!addbranch(targetdelta,&newdelnum)) return false;
  1152. X        }
  1153. X        return true;
  1154. X}
  1155. X
  1156. X
  1157. X
  1158. X    static int
  1159. Xaddbranch(branchpoint,num)
  1160. X    struct hshentry *branchpoint;
  1161. X    struct buf *num;
  1162. X/* adds a new branch and branch delta at branchpoint.
  1163. X * If num is the null string, appends the new branch, incrementing
  1164. X * the highest branch number (initially 1), and setting the level number to 1.
  1165. X * the new delta and branchhead are in globals newdelta and newbranch, resp.
  1166. X * the new number is placed into num.
  1167. X * returns false on error.
  1168. X */
  1169. X{
  1170. X    struct branchhead *bhead, **btrail;
  1171. X    struct buf branchnum;
  1172. X    int result;
  1173. X    unsigned field, numlength;
  1174. X    static struct branchhead newbranch;  /* new branch to be inserted */
  1175. X
  1176. X    numlength = countnumflds(num->string);
  1177. X
  1178. X        if (branchpoint->branches==nil) {
  1179. X                /* start first branch */
  1180. X                branchpoint->branches = &newbranch;
  1181. X                if (numlength==0) {
  1182. X            bufscpy(num, branchpoint->num);
  1183. X            bufscat(num, ".1.1");
  1184. X        } else if (numlength&1)
  1185. X            bufscat(num, ".1");
  1186. X                newbranch.nextbranch=nil;
  1187. X
  1188. X    } else if (numlength==0) {
  1189. X                /* append new branch to the end */
  1190. X                bhead=branchpoint->branches;
  1191. X                while (bhead->nextbranch) bhead=bhead->nextbranch;
  1192. X                bhead->nextbranch = &newbranch;
  1193. X        bufautobegin(&branchnum);
  1194. X        getbranchno(bhead->hsh->num, &branchnum);
  1195. X        incnum(branchnum.string, num);
  1196. X        bufautoend(&branchnum);
  1197. X        bufscat(num, ".1");
  1198. X                newbranch.nextbranch=nil;
  1199. X        } else {
  1200. X                /* place the branch properly */
  1201. X        field = numlength - (numlength&1 ^ 1);
  1202. X                /* field of branch number */
  1203. X        btrail = &branchpoint->branches;
  1204. X        while (0 < (result=cmpnumfld(num->string,(*btrail)->hsh->num,field))) {
  1205. X            btrail = &(*btrail)->nextbranch;
  1206. X            if (!*btrail) {
  1207. X                result = -1;
  1208. X                break;
  1209. X            }
  1210. X                }
  1211. X        if (result < 0) {
  1212. X                        /* insert/append new branchhead */
  1213. X            newbranch.nextbranch = *btrail;
  1214. X            *btrail = &newbranch;
  1215. X            if (numlength&1) bufscat(num, ".1");
  1216. X                } else {
  1217. X                        /* branch exists; append to end */
  1218. X            bufautobegin(&branchnum);
  1219. X            getbranchno(num->string, &branchnum);
  1220. X            targetdelta=genrevs(branchnum.string,(char*)nil,
  1221. X                        (char*)nil,(char*)nil,&gendeltas);
  1222. X            bufautoend(&branchnum);
  1223. X            if (!targetdelta) return false;
  1224. X                        olddeltanum=targetdelta->num;
  1225. X            if (cmpnum(num->string,olddeltanum) <= 0) {
  1226. X                                error("deltanumber %s too low; must be higher than %s",
  1227. X                      num->string,olddeltanum);
  1228. X                                return false;
  1229. X                        }
  1230. X            if (!removelock(targetdelta)) return false;
  1231. X            if (numlength&1) incnum(olddeltanum,num);
  1232. X                        targetdelta->next= &newdelta;
  1233. X                        newdelta.next=nil;
  1234. X                        return true; /* Don't do anything to newbranch */
  1235. X                }
  1236. X        }
  1237. X        newbranch.hsh = &newdelta;
  1238. X        newdelta.next=nil;
  1239. X        return true;
  1240. X}
  1241. X
  1242. X
  1243. X
  1244. X    static void
  1245. Xincnum(onum,nnum)
  1246. X    const char *onum;
  1247. X    struct buf *nnum;
  1248. X/* Increment the last field of revision number onum by one and
  1249. X * place the result into nnum.
  1250. X */
  1251. X{
  1252. X    register const char *sp;
  1253. X    register char *tp;
  1254. X    register unsigned i;
  1255. X
  1256. X    sp = onum;
  1257. X    bufalloc(nnum, strlen(sp)+2);
  1258. X    tp = nnum->string;
  1259. X    for (i=countnumflds(sp);  (--i);  ) {
  1260. X        while (*sp != '.') *tp++ = *sp++;
  1261. X        *tp++ = *sp++;  /* copy dot also */
  1262. X    }
  1263. X    VOID sprintf(tp, "%d", atoi(sp)+1);
  1264. X}
  1265. X
  1266. X
  1267. X
  1268. X    static struct hshentry *
  1269. Xremovelock(delta)
  1270. Xstruct hshentry * delta;
  1271. X/* function: Finds the lock held by caller on delta,
  1272. X * removes it, and returns a pointer to the delta.
  1273. X * Prints an error message and returns nil if there is no such lock.
  1274. X * An exception is if !StrictLocks, and caller is the owner of
  1275. X * the RCS file. If caller does not have a lock in this case,
  1276. X * delta is returned.
  1277. X */
  1278. X{
  1279. X        register struct lock * next, * trail;
  1280. X    const char *num;
  1281. X        struct lock dummy;
  1282. X        int whomatch, nummatch;
  1283. X
  1284. X        num=delta->num;
  1285. X        dummy.nextlock=next=Locks;
  1286. X        trail = &dummy;
  1287. X        while (next!=nil) {
  1288. X        whomatch = strcmp(getcaller(), next->login);
  1289. X                nummatch=strcmp(num,next->delta->num);
  1290. X                if ((whomatch==0) && (nummatch==0)) break;
  1291. X            /*found a lock on delta by caller*/
  1292. X                if ((whomatch!=0)&&(nummatch==0)) {
  1293. X                    error("revision %s locked by %s",num,next->login);
  1294. X                    return nil;
  1295. X                }
  1296. X                trail=next;
  1297. X                next=next->nextlock;
  1298. X        }
  1299. X        if (next!=nil) {
  1300. X                /*found one; delete it */
  1301. X                trail->nextlock=next->nextlock;
  1302. X                Locks=dummy.nextlock;
  1303. X                next->delta->lockedby=nil; /* reset locked-by */
  1304. X                return next->delta;
  1305. X        } else {
  1306. X        if (StrictLocks || !myself(RCSstat.st_uid)) {
  1307. X            error("no lock set by %s for revision %s",getcaller(),num);
  1308. X                    return nil;
  1309. X                } else {
  1310. X                        return delta;
  1311. X                }
  1312. X        }
  1313. X}
  1314. X
  1315. X
  1316. X
  1317. X    static const char *
  1318. Xgetdate()
  1319. X/* Return a pointer to the current date.  */
  1320. X{
  1321. X    static char buffer[datesize]; /* date buffer */
  1322. X    time_t t;
  1323. X
  1324. X    if (!buffer[0]) {
  1325. X        t = time((time_t *)0);
  1326. X        if (t == -1)
  1327. X            faterror("time not available");
  1328. X        time2date(t, buffer);
  1329. X    }
  1330. X        return buffer;
  1331. X}
  1332. X
  1333. X
  1334. X    static const char *
  1335. Xxpandfile (unexfname,dir,delta)
  1336. X    const char *unexfname, *dir;
  1337. X    const struct hshentry *delta;
  1338. X/* Function: Reads file unexpfname and copies it to a
  1339. X * file in dir, performing keyword substitution with data from delta.
  1340. X * returns the name of the expanded file if successful, nil otherwise.
  1341. X */
  1342. X{
  1343. X    const char *targetfname;
  1344. X        FILE * unexfile, *exfile;
  1345. X
  1346. X    targetfname = makedirtemp(dir,0);
  1347. X    errno = 0;
  1348. X    if (!(unexfile = fopen(unexfname, "r"))) {
  1349. X        eerror(unexfname);
  1350. X        return nil;
  1351. X    }
  1352. X    errno = 0;
  1353. X    if (!(exfile = fopen(targetfname, "w"))) {
  1354. X        eerror(targetfname);
  1355. X        error("can't expand file %s",unexfname);
  1356. X        ffclose(unexfile);
  1357. X                return nil;
  1358. X        }
  1359. X    if (Expand == OLD_EXPAND)
  1360. X        fastcopy(unexfile,exfile);
  1361. X    else
  1362. X        while (0 < expandline(unexfile,exfile,delta,false,(FILE*)nil))
  1363. X            ;
  1364. X        ffclose(unexfile);ffclose(exfile);
  1365. X        return targetfname;
  1366. X}
  1367. X
  1368. X
  1369. X    static int
  1370. Xmustcheckin (unexfname,delta)
  1371. X    const char *unexfname;
  1372. X    const struct hshentry *delta;
  1373. X/* Function: determines whether checkin should proceed.
  1374. X * Compares the workfilename with unexfname, disregarding keywords.
  1375. X * If the 2 files differ, returns true. If they do not differ, asks the user
  1376. X * whether to return true or false (i.e., whether to checkin the file anyway);
  1377. X * the default answer is false.
  1378. X * Shortcut: If forceciflag is set, mustcheckin() always returns true.
  1379. X */
  1380. X{
  1381. X    int result;
  1382. X
  1383. X        if (forceciflag) return true;
  1384. X
  1385. X        if (!rcsfcmp(workfilename,unexfname,delta)) return true;
  1386. X        /* If files are different, must check them in. */
  1387. X
  1388. X        /* files are the same */
  1389. X    if (!(result = yesorno(false,
  1390. X        "File %s is unchanged with respect to revision %s\ncheckin anyway? [ny](n): ",
  1391. X        workfilename, delta->num
  1392. X    ))) {
  1393. X        error("%scheckin aborted", 
  1394. X            !quietflag && ttystdin()  ?  ""  :  "file is unchanged; "
  1395. X        );
  1396. X        }
  1397. X        return result;
  1398. X}
  1399. X
  1400. X
  1401. X
  1402. X
  1403. X/* --------------------- G E T L O G M S G --------------------------------*/
  1404. X
  1405. X
  1406. X    static struct cbuf
  1407. Xgetlogmsg()
  1408. X/* Function: obtains a log message and returns a pointer to it.
  1409. X * If a log message is given via the -m option, a pointer to that
  1410. X * string is returned.
  1411. X * If this is the initial revision, a standard log message is returned.
  1412. X * Otherwise, reads a character string from the terminal.
  1413. X * Stops after reading EOF or a single '.' on a
  1414. X * line. getlogmsg prompts the first time it is called for the
  1415. X * log message; during all later calls it asks whether the previous
  1416. X * log message can be reused.
  1417. X * returns a pointer to the character string; the pointer is always non-nil.
  1418. X */
  1419. X{
  1420. X    static const char
  1421. X        emptych[] = "*** empty log message ***",
  1422. X        initialch[] = "Initial revision";
  1423. X    static const struct cbuf
  1424. X        emptylog = { emptych, sizeof(emptych)-sizeof(char) },
  1425. X        initiallog = { initialch, sizeof(initialch)-sizeof(char) };
  1426. X    static struct buf logbuf;
  1427. X    static struct cbuf logmsg;
  1428. X
  1429. X    int cin;
  1430. X    register char *tp;
  1431. X    register size_t i;
  1432. X    register const char *p;
  1433. X    const char *caller, *date;
  1434. X
  1435. X    if (keepflag) {
  1436. X        /* generate std. log message */
  1437. X        caller = getcaller();
  1438. X        p = date = getdate();
  1439. X        while (*p++ != '.')
  1440. X            ;
  1441. X        i = strlen(caller);
  1442. X        bufalloc(&logbuf, sizeof(ciklog)+strlen(caller)+4+datesize);
  1443. X        tp = logbuf.string;
  1444. X        VOID sprintf(tp,
  1445. X            "%s%s at %s%.*s/%.2s/%.2s %.2s:%.2s:%s",
  1446. X            ciklog, caller,
  1447. X            date[2]=='.' && VERSION(5)<=RCSversion  ?  "19"  :  "",
  1448. X            p-date-1, date,
  1449. X            p, p+3, p+6, p+9, p+12
  1450. X        );
  1451. X        logmsg.string = tp;
  1452. X        logmsg.size = strlen(tp);
  1453. X        return logmsg;
  1454. X    }
  1455. X
  1456. X    if (msg.size) return msg;
  1457. X
  1458. X    if (!olddeltanum && (
  1459. X        cmpnum(newdelnum.string,"1.1")==0 ||
  1460. X        cmpnum(newdelnum.string,"1.0")==0
  1461. X    ))
  1462. X        return initiallog;
  1463. X
  1464. X    if (logmsg.size) {
  1465. X                /*previous log available*/
  1466. X        if (yesorno(true, "reuse log message of previous file? [yn](y): "))
  1467. X        return logmsg;
  1468. X        }
  1469. X
  1470. X        /* now read string from stdin */
  1471. X    if (feof(stdin))
  1472. X        faterror("can't reread redirected stdin for log message; use -m");
  1473. X    if (ttystdin())
  1474. X        aputs("enter log message:\n(terminate with single '.' or end of file)\n>> ",stderr);
  1475. X
  1476. X    i = 0;
  1477. X    tp = logbuf.string;
  1478. X    while ((cin = getcstdin()) != EOF) {
  1479. X        if (cin=='\n') {
  1480. X            if (i && tp[i-1]=='.' && (i==1 || tp[i-2]=='\n')) {
  1481. X                /* Remove trailing '.'. */
  1482. X                --i;
  1483. X                break;
  1484. X            }
  1485. X            if (ttystdin()) aputs(">> ", stderr);
  1486. X        }
  1487. X        bufrealloc(&logbuf, i+1);
  1488. X        tp = logbuf.string;
  1489. X        tp[i++] = cin;
  1490. X                /*SDELIM will be changed to double SDELIM by putdtext*/
  1491. X        } /* end for */
  1492. X
  1493. X        /* now check whether the log message is not empty */
  1494. X    logmsg = cleanlogmsg(tp, i);
  1495. X    if (logmsg.size)
  1496. X        return logmsg;
  1497. X    return emptylog;
  1498. X}
  1499. X
  1500. X/*  Make a linked list of Symbolic names  */
  1501. X
  1502. X        static void
  1503. Xaddassoclst(flag, sp)
  1504. Xint  flag;
  1505. Xchar * sp;
  1506. X{
  1507. X        struct Symrev *pt;
  1508. X    
  1509. X    pt = talloc(struct Symrev);
  1510. X    pt->ssymbol = sp;
  1511. X    pt->override = flag;
  1512. X    pt->nextsym = nil;
  1513. X    if (lastassoc)
  1514. X            lastassoc->nextsym = pt;
  1515. X    else
  1516. X            assoclst = pt;
  1517. X    lastassoc = pt;
  1518. X    return;
  1519. X}
  1520. END_OF_FILE
  1521.   if test 34353 -ne `wc -c <'src/ci.c'`; then
  1522.     echo shar: \"'src/ci.c'\" unpacked with wrong size!
  1523.   fi
  1524.   # end of 'src/ci.c'
  1525. fi
  1526. echo shar: End of archive 1 \(of 12\).
  1527. cp /dev/null ark1isdone
  1528. MISSING=""
  1529. for I in 1 2 3 4 5 6 7 8 9 10 11 12 ; do
  1530.     if test ! -f ark${I}isdone ; then
  1531.     MISSING="${MISSING} ${I}"
  1532.     fi
  1533. done
  1534. if test "${MISSING}" = "" ; then
  1535.     echo You have unpacked all 12 archives.
  1536.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1537. else
  1538.     echo You still must unpack the following archives:
  1539.     echo "        " ${MISSING}
  1540. fi
  1541. exit 0
  1542. exit 0 # Just in case...
  1543.